// Adam Czubernat, Grzegorz Calik
// Programowanie rownolegle czerwiec 2010

#include <mpi.h>
#include <iostream>
#include <fstream>

using namespace std;

void drawMatrix(int n, int m, double* M){
	for (int i=0;i<n;i++){
		for (int j=0;j<m;j++)
			cout << M[i*m+j] << ", ";
		cout << endl;
	}
}

void drawVector(int n, double A[]){
	for (int i=0;i<n;i++){
		cout << A[i] << endl;
	}
}

int main(int argc, char* argv[]){
	int numproc, rank;
	int n, k1, k2, bands;
	
	MPI_Status status;
	
	MPI_Init(&argc,&argv);
	MPI_Comm_size(MPI_COMM_WORLD,&numproc);
	MPI_Comm_rank(MPI_COMM_WORLD,&rank);
	numproc--;
	
	if (argc < 2 || argc > 2){
		if (rank == 0) 
			cout	<< "Zle dobrane argumenty, prawidlowe uzycie:\n"	
					<< "matrix nazwa_pliku\n";
		MPI_Finalize();
		return -1;
	}
	
	// MASTER
	
	if (rank == 0){

		char* filename = argv[1];
		
		ifstream file;			//Wczytywanie pliku z danymi
		file.open(filename); 	//Sciezka pliku powinna byc czwartym argumentem
		if (file.fail()){		//Jesli pliku nie otwarto
			cout << "Nie znaleziono pliku!\n";
			MPI_Finalize();
			return -1;
		}
		file >> n >> k1 >> k2;				//Wczytanie pierwszego wiersza pliku
		bands = k1+k2+1;					//macierzy skompresowanej
		double *M = new double [n*bands];	//Wczytywanie i dynamiczne tworzenie

		for (int i=0;i<n;i++){
			for (int j=0;j<bands;j++)
				file >> M[i*bands+j];
		}
		double* v = new double [n];		//Wczytywanie wektora
		for (int i=0;i<n;i++)
			file >> v[i];
		file.close();
		
		double *ans = new double [n]; 	//Tworzenie wektora wynikowego
		
		cout << "Macierz o rozmiarze n: " << n << " o ilosci pasm p: " << bands << endl; 
		drawMatrix(n,bands,M);
		cout << "Wektor:" << endl;
		drawVector(n,v);
		
		double time = MPI_Wtime();
		
		for (int i=0;i<numproc;i++){
			int firstRow = i*n/numproc;
			int lastRow = (i+1)*n/numproc;
			int rows = lastRow - firstRow;
			
//			cout << "Wysylanie do " << i+1 << " przedzialu " << firstRow << "-" << lastRow << endl;
			MPI_Send(&n,1,MPI_INT,i+1,0,MPI_COMM_WORLD);
			MPI_Send(&k1,1,MPI_INT,i+1,0,MPI_COMM_WORLD);
			MPI_Send(&k2,1,MPI_INT,i+1,0,MPI_COMM_WORLD);
			
			bands = k1+k2+1;
			
			MPI_Send(&M[firstRow*bands],bands*rows,MPI_DOUBLE,i+1,0,MPI_COMM_WORLD);
			MPI_Send(&v[firstRow-k1],rows+k1+k2,MPI_DOUBLE,i+1,0,MPI_COMM_WORLD);
		}
		
		for (int i=0;i<numproc;i++){
			int firstRow = i*n/numproc;
			int lastRow = (i+1)*n/numproc;
			int rows = lastRow - firstRow;
			
			MPI_Recv(&ans[firstRow],rows,MPI_DOUBLE,i+1,0,MPI_COMM_WORLD,&status);
		}
		
		time = MPI_Wtime() - time;
		
		cout << "Wynik:" << endl;
		drawVector(n,ans);
		cout << "Czas trwania operacji: " << time*1000 << "ms" << endl;
		
		delete[] M;
		delete[] v;
		delete[] ans;
		
	// WORKER
		
	} else {
		
		MPI_Recv(&n,1,MPI_INT,0,0,MPI_COMM_WORLD,&status);
		MPI_Recv(&k1,1,MPI_INT,0,0,MPI_COMM_WORLD,&status);
		MPI_Recv(&k2,1,MPI_INT,0,0,MPI_COMM_WORLD,&status);
		
		bands = k1+k2+1;
		
		int firstRow = (rank-1)*n/numproc;
		int lastRow = rank*n/numproc;
		int rows = lastRow - firstRow;
		
		double *M = new double [rows*bands];
		MPI_Recv(M,bands*rows,MPI_DOUBLE,0,0,MPI_COMM_WORLD,&status);
		
		double *ans = new double [rows];
		double *v = new double [rows+k1+k2];
		
		MPI_Recv(v,rows+k1+k2,MPI_DOUBLE,0,0,MPI_COMM_WORLD,&status);
		
		for (int i=0;i<rows;i++){
			for (int j=0;j<bands;j++){
				int pnt = (firstRow+i-k1)+j;
				if (pnt >=0 && pnt<n)
					ans[i] += M[i*bands+j]*v[i+j];
			}
		}
		
		MPI_Send(ans,rows,MPI_DOUBLE,0,0,MPI_COMM_WORLD);
		
		delete[] M;
		delete[] v;
		delete[] ans;
		
//		printf("Worker rank: %d\nWiersze: %d-%d\n",rank,firstRow,lastRow);
//		drawMatrix(rows,bands,M);
//		printf("Worker rank: %d\nWektor: %d-%d\n",rank,firstRow,lastRow);
//		drawVector(rows+k1+k2,v);
	}
	
	MPI_Finalize();
	return 0;
}